﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DiscoveryLogic.UI.Layouts.Controls
{
    /// <summary>
    /// RadialItemControl is a control that is placed on a RadialPanel, and is designed to be positioned by the panel
    /// at a given angle. It exposes an "Image" property which appears in the centre of the control, and has an associated
    /// "Label" property, which defines an associated label for the control. The label is positioned at the same angle
    /// as the angle of the control to its parent. The positioning work of this control is done by its inner RadialItemPanel
    /// which is not exposed to the user.
    /// </summary>
    public class RadialItemControl : UserControl
    {
        #region Private data variables

        // _innerPanel is the panel that we store the image and label in, so that we can position them as we wish
        private RadialItemPanel _innerPanel;

        #endregion // Private data variables

        #region Constructors

        public RadialItemControl()
        {
            // Our control has one item of content, which is a RadialItemPanel, which will control the position
            // of the Image and Label contents.
            _innerPanel = new RadialItemPanel();
            Content = _innerPanel;
        }

        #endregion // Constructors

        #region Dependency Properties

        /// <summary>
        /// Image is a FrameworkElement derived type that will display the visual element of the control. It could
        /// be an Image, a TextBlock, or whatever. However, any control used for the Image which takes mouse events
        /// will not work, because it will stop the item from being dragged. For example, making the Image a Button
        /// object will not work.
        /// </summary>
        public static readonly DependencyProperty ImageProperty =
            DependencyProperty.Register("Image", typeof(FrameworkElement), typeof(RadialItemControl), null);

        /// <summary>
        /// Label is a FrameworkElement derived type that will display at an offset from the Image property, and
        /// typically represents a label associated with the control. It does not necessarily need to be a TextBlock
        /// </summary>
        public static readonly DependencyProperty LabelProperty =
            DependencyProperty.Register("Label", typeof(FrameworkElement), typeof(RadialItemControl), null);

        /// <summary>
        /// Radius is a dependency property that depicts how far away from the Image we should position the Label.
        /// The Label is positioned at the same angle as the control's attached angle property (from RadialPanel),
        /// and this radius defines the radius of the circle where we will position the Label.
        /// </summary>
        public static readonly DependencyProperty RadiusProperty =
            DependencyProperty.Register("Radius", typeof(Point), typeof(RadialItemControl), null);

        /// <summary>
        /// Image property is used to define the content that will be displayed as the "image" of this panel item
        /// </summary>
        public FrameworkElement Image
        {
            get { return (FrameworkElement) GetValue(ImageProperty); }
            set
            {
                if (_innerPanel.Children.Contains((FrameworkElement) GetValue(ImageProperty)))
                    _innerPanel.Children.Remove((FrameworkElement) GetValue(ImageProperty));
                SetValue(ImageProperty, value);
                _innerPanel.Children.Add((FrameworkElement) value);
            }
        }

        /// <summary>
        /// Label property is used to define the content that will be associated as the "label" of this panel item.
        /// Typically, the label does not need to be contained within the constraints of the panel. It is positioned
        /// dynamically based on the RadialPanel.InnerAngle of this panel item, which is set by the RadialPanel panel.
        /// </summary>
        public FrameworkElement Label
        {
            get { return (FrameworkElement) GetValue(LabelProperty); }
            set
            {
                if (_innerPanel.Children.Contains((FrameworkElement) GetValue(LabelProperty)))
                    _innerPanel.Children.Remove((FrameworkElement) GetValue(LabelProperty));
                SetValue(LabelProperty, value);
                _innerPanel.Children.Add((FrameworkElement) value);
            }
        }

        /// <summary>
        /// Radius property defines the radius of the circle which we will use to position the label around the image
        /// such that it matches the angle that this item is in relation to its owning RingPanel control. If the item
        /// is not attached to a RingPanel, then the related angle will always be 0.
        /// </summary>
        public Point Radius
        {
            get { return (Point) GetValue(RadiusProperty); }
            set { SetValue(RadiusProperty, value); }
        }

        #endregion // Dependency Properties

        #region Events To Fire

        // Event handler for when the control is clicked.
        public event RoutedEventHandler Clicked;
        // Event handler for when a drag operation begins on this control
        public event RoutedEventHandler BeginDrag;
        // Event handler for when a drag operation ends on this control
        public event RoutedEventHandler EndDrag;

        protected virtual void OnClicked(RoutedEventArgs e)
        {
            RoutedEventHandler handler = Clicked;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnBeginDrag(RoutedEventArgs e)
        {
            RoutedEventHandler handler = BeginDrag;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnEndDrag(RoutedEventArgs e)
        {
            RoutedEventHandler handler = EndDrag;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion // Events to Fire

        #region Methods

        /// <summary>
        /// Because the RadialPanel deals with all mouse events, and has to worry about whether a mouse down event
        /// is really a click or the start of a drag operation, it needs to tell us when the control has been clicked
        /// This enables this Control to fire the necessary Clicked event, so that the owning control / page can attach
        /// to the event at the control level, rather than obscurely attaching to a Click event on the RadialPanel.
        /// </summary>
        public void HandleClicked()
        {
            RoutedEventArgs args = new RoutedEventArgs();

            //args.Source = this;
            OnClicked(args);
        }

        /// <summary>
        /// The RadialPanel calls this when it starts a drag on this control, so that we can fire the event to the
        /// creator of the control, which makes more sense to people using this control.
        /// </summary>
        public void HandleBeginDrag()
        {
            RoutedEventArgs args = new RoutedEventArgs();

            //args.Source = this;
            OnBeginDrag(args);
        }

        /// <summary>
        /// The RadialPanel calls this when it ends a drag on this control, so that we can fire the event to the
        /// creator of the control, which makes more sense to people using this control.
        /// </summary>
        public void HandleEndDrag()
        {
            RoutedEventArgs args = new RoutedEventArgs();

            //args.Source = this;
            OnEndDrag(args);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            // When we are positioned it does not automatically invalidate the arrangement of the radial panel item
            // which needs to be done in case the attached Angle property has been updated. We invalidate the
            // panel's arrangement to ensure that the panel re-arranges its children.
            _innerPanel.InvalidateArrange();
            return base.ArrangeOverride(finalSize);
        }

        #endregion // Methods
    }
}
